﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using Vra.DataAccess.Attributes;

namespace Vra.DataAccess.Misc
{
	/// <summary>
	/// Нам лень писать запросы, пусть они сами генерятся
	/// </summary>
	internal class QueryManager
	{
		private const string DeleteQuery = "DELETE FROM {0} WHERE {1} = {2}";
		private const string SelectAllQuery = "SELECT {0} FROM {1}";
		private const string SelectQuery = SelectAllQuery + " WHERE {2} = {3}";
		private const string InsertQuery = "INSERT INTO {0} ({1}) VALUES ({2})";
		private const string UpdateQuery = "UPDATE {0}\r\nSET {1}\r\nWHERE {2} = {3}";

		private const string ParamPrefix = "@";

		/// <summary>
		/// Возвращает SQL запрос для DELETE Query
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="findParam"></param>
		/// <returns></returns>
		public static string GetDelQuery<T>(string findParam)
		{
			string tableName = GetTableName<T>();
			if (tableName == null)
				return null;
			string propName = GetIdentityColumn<T>();
			return string.Format(DeleteQuery, tableName, propName, findParam);
		}

		public static string GetDelQuery<T>()
		{
			string tableName = GetTableName<T>();
			if (tableName == null)
				return null;
			string propName = GetIdentityColumn<T>();
			return string.Format(DeleteQuery, tableName, propName, ParamPrefix + propName);
		}

		private static string GetSelectQueryInternal<T>(string id)
		{
			string tableName = GetTableName<T>();
			if (tableName == null)
				return null;
			string query;
			if (string.IsNullOrEmpty(id))
			{
				query = string.Format(SelectAllQuery, string.Join(",", GetAllColumns<T>(true)), tableName);
			}
			else
			{
				string propName = GetIdentityColumn<T>();
				query = string.Format(SelectQuery, string.Join(",", GetAllColumns<T>(true)), tableName, propName, id);
			}
			return query;
		}

		/// <summary>
		/// Возвращает SQL SELECT запрос для всех объектов
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <returns></returns>
		public static string GetSelectQuery<T>()
		{
			return GetSelectQueryInternal<T>(null);
		}

		/// <summary>
		/// Возвращает SQL SELECT запрос для объекта с указанным id
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="id">название параметра поиска</param>
		/// <returns></returns>
		public static string GetSelectQuery<T>(string id)
		{
			return GetSelectQueryInternal<T>(id);
		}

		private static string GetTableName<T>()
		{
			var tableAttr = typeof(T).GetCustomAttributes(typeof(TableAttribute), true).Cast<TableAttribute>().FirstOrDefault();
			return tableAttr == null ? null : tableAttr.Name;
		}

		private static string GetIdentityColumn<T>()
		{
			var field = typeof(T).GetFields().First(p => p.GetCustomAttributes(typeof(ColumnAttribute), true)
								.Cast<ColumnAttribute>()
								.Any(q => q.IsIdentity));
			var identityAttr = field.GetCustomAttributes(typeof(ColumnAttribute), true).Cast<ColumnAttribute>().First();
			string propName = string.IsNullOrEmpty(identityAttr.Name) ? field.Name : identityAttr.Name;
			return propName;
		}

		private static IEnumerable<string> GetAllColumns<T>(bool withIdentity)
		{
			IList<string> columns= new List<string>();
			foreach (var field in typeof(T).GetFields())
			{
				var attr = field.GetCustomAttributes(typeof (ColumnAttribute), true).Cast<ColumnAttribute>().FirstOrDefault();
				if (attr == null)
					continue;
				if (attr.IsIdentity && !withIdentity)
					continue;
				string name = string.IsNullOrEmpty(attr.Name) ? field.Name : attr.Name;
				columns.Add(name);
			}
			return columns;
		}

		public static string GetInsertQuery<T>()
		{
			var tableName = GetTableName<T>();
			if (tableName == null)
				return null;
			var columns = GetAllColumns<T>(false);
			var pars = columns.Select(p => ParamPrefix + p);
			var res = string.Format(InsertQuery, tableName, string.Join(",", columns), string.Join(",", pars));
			return res;
		}

		public static string GetUpdateQuery<T>()
		{
			var tableName = GetTableName<T>();
			if (tableName == null)
				return null;
			var cols = GetAllColumns<T>(false);
			IList<string> columns = new List<string>();
			foreach (var column in cols)
			{
				columns.Add(string.Format("{0} = {1}", column, ParamPrefix + column));
			}
			var identity = GetIdentityColumn<T>();
			var res = string.Format(UpdateQuery, tableName, string.Join(",\r\n", columns), identity, ParamPrefix + identity);
			return res;
		}

		public static IDictionary<string, string> GetColumnsWithParams<T>(QueryType type)
		{
			IDictionary<string,string> dictionary = new Dictionary<string, string>();

			switch (type)
			{
					case QueryType.SelectById:
					case QueryType.Delete:
					{
						var field = typeof(T)
									.GetFields()
									.First(p => p.GetCustomAttributes(typeof(ColumnAttribute), true)
									.Cast<ColumnAttribute>()
									.Any(q => q.IsIdentity));
						var identityAttr = field.GetCustomAttributes(typeof(ColumnAttribute), true).Cast<ColumnAttribute>().First();
						string propName = string.IsNullOrEmpty(identityAttr.Name) ? field.Name : identityAttr.Name;
						dictionary.Add(field.Name, ParamPrefix + propName);
						break;
					}
					case QueryType.Insert:
					case QueryType.Update:
					{
						foreach (var field in typeof(T).GetFields())
						{
							var attr = field.GetCustomAttributes(typeof(ColumnAttribute), true).Cast<ColumnAttribute>().FirstOrDefault();
							if (attr == null || ( type != QueryType.Update && attr.IsIdentity))
								continue;
							string name = string.IsNullOrEmpty(attr.Name) ? field.Name : attr.Name;
							dictionary.Add(field.Name, ParamPrefix + name);
						}
						break;
					}
				default:
					break;
			}

			return dictionary;
		}
	}
}
